Skip to content

fix: guard ThemeProvider localStorage access against SecurityError#1178

Merged
sw-factory-automations merged 2 commits into
mainfrom
fix/sentry-memo-2h-localstorage-security-error
May 24, 2026
Merged

fix: guard ThemeProvider localStorage access against SecurityError#1178
sw-factory-automations merged 2 commits into
mainfrom
fix/sentry-memo-2h-localstorage-security-error

Conversation

@sw-factory-automations
Copy link
Copy Markdown
Collaborator

Summary

Wraps localStorage calls in ThemeProvider with try-catch to prevent crashes when storage access is denied.

Closes #1177

Sentry Issue

MEMO-2HSecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document. — 4 events, 2 users, Chrome Mobile 149 on Android.

Root Cause

ThemeProvider in src/lib/theme.tsx calls localStorage.getItem() (line 31) and localStorage.setItem() (line 41) without try-catch. Browsers with restricted storage access (privacy settings, embedded contexts, enterprise policies) throw a SecurityError (DOMException), which propagates through React's render tree and breaks the entire app.

The inline theme script in layout.tsx already had identical try-catch protection. The use-persisted-expanded.ts hook also wraps localStorage in try-catch. Only theme.tsx was unprotected.

Changes

  • src/lib/theme.tsx — Wrap localStorage.getItem in useState initializer and localStorage.setItem in setPreference with try-catch. Falls back to "dark" on read, silently ignores on write.
  • src/lib/theme.test.tsx — New regression test covering: default behavior, stored preference reading, invalid value handling, preference persistence, and SecurityError handling for both getItem and setItem.
  • src/lib/sentry/sentry.test.ts — Add theme.tsx to the bare-catch permanent allowlist (same pattern as use-persisted-expanded.ts).
  • .agents/conventions.md — Document localStorage safety requirement in the Theme section.

Verification

  • pnpm lint — 0 errors
  • pnpm typecheck — clean
  • pnpm test — 1981/1981 tests pass (including 6 new theme tests)

Wrap localStorage.getItem and localStorage.setItem in try-catch in
theme.tsx to prevent crashes when storage access is denied (restricted
browsers, privacy settings, embedded contexts). Falls back to dark
theme on read, silently ignores on write.

The inline theme script in layout.tsx already had this protection, but
ThemeProvider did not — causing a SecurityError that broke the entire
React render tree for affected users.

Co-authored-by: Ona <no-reply@ona.com>
@sw-factory-automations sw-factory-automations added the bug Something isn't working label May 24, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
memo Ready Ready Preview, Comment May 24, 2026 1:08am

Request Review

@sw-factory-automations
Copy link
Copy Markdown
Collaborator Author

Review complete — no issues found.

The fix is minimal, correct, and well-tested:

  • try-catch around both localStorage calls matches the established pattern in use-persisted-expanded.ts and the inline theme script
  • Fallback to "dark" on read failure is consistent with the default theme
  • In-memory state updates correctly even when storage is unavailable
  • Tests cover all relevant scenarios including SecurityError

Merge blocked by pending Vercel status check — the Vercel preview deploy has been pending since 00:11 UTC (30+ min). All CI checks (lint, typecheck, tests, e2e, visual, bundle) have passed. Will merge once the Vercel check resolves.

Co-authored-by: Ona <no-reply@ona.com>
@sw-factory-automations sw-factory-automations merged commit 0010c9a into main May 24, 2026
9 checks passed
@sw-factory-automations sw-factory-automations deleted the fix/sentry-memo-2h-localstorage-security-error branch May 24, 2026 01:20
@sw-factory-automations
Copy link
Copy Markdown
Collaborator Author

✅ UI verification skipped — no UI changes detected.

Changed files (src/lib/theme.tsx, src/lib/theme.test.tsx, src/lib/sentry/sentry.test.ts, .agents/conventions.md) contain only logic changes (try-catch guards) and tests. No components, pages, or layouts were modified.

@sw-factory-automations
Copy link
Copy Markdown
Collaborator Author

✅ Post-merge verification passed.

E2E tests (against live site):

  • e2e/auth.spec.ts — 8/8 passed (sign-in, sign-up, sign-out, redirects)
  • e2e/page-crud.spec.ts — 5/5 passed (create, navigate, rename, delete pages)
  • e2e/database-crud.spec.ts — 11/11 passed (create DB, add columns/rows, delete, undo)
  • e2e/workspace-settings.spec.ts — 2/3 passed (1 flaky failure on "change workspace name" passed on retry — pre-existing, unrelated to this PR)

Ad-hoc smoke tests:

  • ✅ Landing page — loaded, has title
  • ✅ Sign-in page — rendered with email input
  • ✅ Health endpoint — returned OK
  • ✅ Authenticated flow — login succeeded, workspace loaded
  • ✅ No console errors (unauthenticated or authenticated)
  • Skipped: /dashboard (404, not yet built), editor page navigation (no page buttons in test workspace)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: ThemeProvider crashes on localStorage SecurityError in restricted browsers (Sentry MEMO-2H)

1 participant